#include <cstdio>
#include <cstdlib>
#include <Windows.h>
#include <WinGDI.h> // bitmap
#include <ctime>

int main(int argc, char* argv[]) {
/*
	Format for input: output input size
					 out.bmp
							in.txt
								  40x120
	Etc, etc. Empty spaces will be filled with random bytes.
*/
	if(argc < 3) {
		char szExeName[256] = {0};
		int endPos = strlen(argv[0]);
		for(int i = endPos; i > 0; --i) {
			if(argv[0][i] == '\\') {
				endPos = ++i;
				break;
				
			}
		}
		strcpy(szExeName, &argv[0][endPos]);

		printf("txt2bmp by {im}plicit\n\nUsage: %s out.bmp in.txt LxW\n\n\x9out.bmp - output file\n\x9in.txt  - text source\n\x9LxW     - size in pixels (ie 40x120)", szExeName);
		return 1;
	}
// Get size from argv[3]
	DWORD dwLen = 0,
	      dwWidth = 0;
	char *cLen = strtok(argv[3], "x"),
		 *cWid = strtok(NULL, "x");
	
	dwLen = atoi(cLen);
	dwWidth = atoi(cWid);

// end argv[] retrieving
	FILE *fIn = fopen(argv[2], "rb");
	if(fIn == NULL) {
		printf("\nCould not open %s.", argv[2]);
		return 1;
	}

	// get file size for DMA buffer
	fseek(fIn, 0, SEEK_END);
	DWORD dwFileSize = ftell(fIn);
	rewind(fIn);
	
	char* szTextBuffer = new char[dwFileSize];
	ZeroMemory(szTextBuffer, dwFileSize);

	if(!fread(szTextBuffer, 1, dwFileSize, fIn)) {
		printf("Could not read %s.", argv[2]);
		fclose(fIn);
		return 1;
	}
	fclose(fIn);

// first make the output file
	HANDLE hOut = CreateFileA(argv[1], GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hOut == INVALID_HANDLE_VALUE || hOut == NULL) {
		printf("\nCould not create %s or it already exists.", argv[1]);
		return 1;
	}
	CloseHandle(hOut);

// done with output creation
	BITMAPINFO *bih = new BITMAPINFO;
	BITMAPFILEHEADER *bfh = new BITMAPFILEHEADER;

	FILE *fIO;	
	fIO = fopen(argv[1], "wb+");
	if(fIO == NULL) {
		printf("\nCould not write to %s.", argv[1]);
		return 1;
	}
	
	ZeroMemory(bih, sizeof(BITMAPINFO));
	ZeroMemory(bfh, sizeof(BITMAPFILEHEADER));

	bfh->bfType = '\x42\x4d';	// mz
	bfh->bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
	bih->bmiHeader.biSizeImage = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + dwFileSize;
	bih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bih->bmiHeader.biBitCount = 24;
	bih->bmiHeader.biCompression = BI_RGB;
	bih->bmiHeader.biHeight = dwLen;
	bih->bmiHeader.biWidth = dwWidth;
	bih->bmiHeader.biPlanes = 1;

	if(!fwrite(bfh, sizeof(BITMAPFILEHEADER), 1, fIO)) {
		printf("\nCould not write BITMAPFILEHEADER.");
		return 1;
	}
	if(!fwrite(&bih->bmiHeader, sizeof(BITMAPINFOHEADER), 1, fIO)) {
		printf("\nCould not write BITMAPINFOHEADER.");
		return 1;
	}
// headers are written
// now data
	bool noJunk;
	DWORD dwRemaining = 0;
	if(strlen(szTextBuffer) < dwLen * dwWidth) {
		dwRemaining = (dwLen * dwWidth) - strlen(szTextBuffer);
		noJunk = false;
	} else noJunk = true;
	if((strlen(szTextBuffer) % (dwWidth * 3)) % 4 != 0)
		dwRemaining += (strlen(szTextBuffer) % (dwWidth * 3)) % 4;
	
	DWORD dwCurrent = 0,
		  dwOccupiedRows = strlen(szTextBuffer) / dwWidth,
		  dwRemainingInRow = strlen(szTextBuffer) % dwWidth;

	char* szPadding = new char[dwRemaining];
	memset(szPadding, 0, dwRemaining);
	char* szJunk = new char[dwWidth * 3];
	memset(szJunk, 0, dwWidth * 3);

	DWORD dwPitch = dwWidth * 3;
	if(dwPitch % 4 != 0) {
		dwPitch += 4 - (dwPitch % 4);
	}

	bool bJunk = false;

	DWORD dwPaddingPerRow = dwPitch - dwWidth * 3,
		  dwBytes2Write = dwWidth * 3,
		  dwJunkBytes = 0;

	for(DWORD dw = 0; dw < dwLen; ++dw)
	{
		if(!bJunk) {
			if((dwCurrent + (dwWidth * 3)) > dwFileSize) {
				bJunk = true;
				dwBytes2Write = dwFileSize - dwCurrent;
				dwJunkBytes = (dwWidth * 3) - dwBytes2Write;
			}
		} else {
			dwBytes2Write = NULL;
			dwJunkBytes = dwWidth * 3;
		}

		if(dwBytes2Write) {
			if(!fwrite(&szTextBuffer[dwCurrent], dwBytes2Write, 1, fIO)) {
				printf("\nCould not write text.");
				return 1;
			}
		}

		if(bJunk) {
			// generate random shit
			for(int i = 0; i < dwJunkBytes; ++i) {
				srand(time(0) * i);
				szJunk[i] = rand() % 0xFF;	
			}
			if(!fwrite(szJunk, 1, dwJunkBytes, fIO)) {
				printf("\nCould not write junk.");
				return 1;
			}
		}

		if(!fwrite(szPadding, 1, dwPaddingPerRow, fIO)) {
			printf("\nCould not write padding. Try increasing image size to accomodate buffer.");
			return 1;
		}

		if(!bJunk)
			dwCurrent += dwBytes2Write;
	}
	// cleanup
	fclose(fIO);
	delete[] szTextBuffer;
	delete[] szPadding;
	delete[] szJunk;
	delete bih;
	delete bfh;	
	return 0;
}